﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Linq;

namespace Code4WP7SDK
{
    //反向传递消息 MessageBackTrain (类似于MvvmLight中的Messenger)
    public class MckTrain : IMckTrain
    {
        #region Fields
        private static readonly object CreationLock = new object();                       //对象创建锁
        private readonly object _registerLock = new object();                             //注册锁
        private Dictionary<Type, List<WeakActionAndToken>> _takersSubClassActionAndToken; //同等级的类的方法和标示
        private Dictionary<Type, List<WeakActionAndToken>> _takersActionAndToken;         //自身的方法和标示  
        #endregion

        #region Constructor
        private MckTrain() { }
        private static MckTrain _instance;
        public static MckTrain Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (CreationLock)
                    {
                        if (_instance == null)
                        {
                            _instance = new MckTrain();
                        }
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// 覆盖原有的信息队列
        /// </summary>
        /// <param name="messagerTrain"></param>
        public static void OverrideDefault(MckTrain mckTrain)
        {
            _instance = mckTrain;
        }

        public static void Reset()
        {
            _instance = null;
        }
        #endregion

        #region Regist
        public void Regist<TMessage>(object taker, Action<TMessage> action)
        {
            Regist<TMessage>(taker, null, false, action);
        }

        public void Regist<TMessage>(object taker, object token, Action<TMessage> action)
        {
            Regist<TMessage>(taker, token, false, action);
        }

        public void Regist<TMessage>(object taker, bool receiveDeriveMessageToo, Action<TMessage> action)
        {
            Regist<TMessage>(taker, null, receiveDeriveMessageToo, action);
        }

        public void Regist<TMessage>(object taker, object token, bool receiveDeriveMessageToo, Action<TMessage> action)
        {
            lock (_registerLock)
            {
                Type messageType = typeof(TMessage);
                Dictionary<Type, List<WeakActionAndToken>> takersActionAndToken;
                if (receiveDeriveMessageToo)
                {
                    if (_takersSubClassActionAndToken == null)
                    {
                        _takersSubClassActionAndToken = new Dictionary<Type, List<WeakActionAndToken>>();
                    }
                    takersActionAndToken = _takersSubClassActionAndToken;
                }
                else
                {
                    if (_takersActionAndToken == null)
                    {
                        _takersActionAndToken = new Dictionary<Type, List<WeakActionAndToken>>();
                    }
                    takersActionAndToken = _takersActionAndToken;
                }
                //锁住所有的标示和操作
                lock (takersActionAndToken)
                {
                    List<WeakActionAndToken> list;
                    if (!takersActionAndToken.ContainsKey(messageType))
                    {
                        list = new List<WeakActionAndToken>();
                        takersActionAndToken.Add(messageType, list);
                    }
                    else
                    {
                        list = takersActionAndToken[messageType];
                    }
                    //关键点在这里(把所有的信息都封装到了以MessageType为键值为所有的方法和标示的dictionary当中 其中用到了线程锁)
                    WeakAction weakAction = new WeakAction<TMessage>(taker, action);
                    var item = new WeakActionAndToken { Action = weakAction, Token = token };
                    list.Add(item);
                }
            }
            CleanUp();
        }
        #endregion

        #region Send
        public void Send<TMessage>(TMessage message)
        {
            SendToTargetOrType<TMessage>(message, null, null);
        }

        public void Send<TMessage, TTarget>(TMessage message)
        {
            SendToTargetOrType<TMessage>(message, typeof(TTarget), null);
        }

        public void Send<TMessage>(TMessage message, object token)
        {
            SendToTargetOrType(message, null, token);
        }

        private void SendToTargetOrType<TMessage>(TMessage message, Type messageTargetType, object token)
        {

            Type messageType = message.GetType();
            if (_takersSubClassActionAndToken != null)
            {
                List<WeakActionAndToken> list = null;
                List<Type> listClone = _takersSubClassActionAndToken.Keys.Take(_takersSubClassActionAndToken.Count).ToList();
                foreach (Type type in listClone)
                {
                    if (messageType == type || messageType.IsSubclassOf(type) || Implements(messageType, type))
                    {
                        lock (_takersSubClassActionAndToken)
                        {
                            list = _takersSubClassActionAndToken[type].Take(_takersSubClassActionAndToken[type].Count).ToList();
                        }
                    }
                    SendToList<TMessage>(message, list, messageTargetType, token);
                }
            }

            if (_takersActionAndToken != null)
            {
                if (_takersActionAndToken.ContainsKey(messageType))
                {
                    List<WeakActionAndToken> list = null;
                    lock (_takersActionAndToken)
                    {
                        list = _takersActionAndToken[messageType].Take(_takersActionAndToken[messageType].Count).ToList();
                    }
                    SendToList<TMessage>(message, list, messageTargetType, token);
                }
            }

            CleanUp();
        }

        private void SendToList<TMessage>(TMessage message, List<WeakActionAndToken> list, Type messageTargetType, object token)
        {
            if (list != null)
            {
                List<WeakActionAndToken> listClone = list.Take(list.Count()).ToList();
                foreach (WeakActionAndToken item in listClone)
                {
                    var executeAction = item.Action as IExecuteWithObject;
                    if (
                        (
                            executeAction != null
                            && item.Action.IsAlive
                            && item.Action.Target != null
                        )
                        &&
                        (
                            messageTargetType == null
                            || messageTargetType == item.Action.Target.GetType()
                            || Implements(item.Action.Target.GetType(), messageTargetType)
                        )
                        &&
                        (
                            (item.Token == null && token == null)
                            || (item.Token != null && (item.Token.Equals(token)))))
                    {
                        executeAction.ExecuteWithObject(message);   //关键点
                    }
                }
            }
        }
        /// <summary>
        /// 判断目标类型是否实现了接口
        /// </summary>
        /// <param name="type"></param>
        /// <param name="messageTargetType"></param>
        /// <returns></returns>
        private bool Implements(Type instanceType, Type interfaceType)
        {
            if (instanceType == null || interfaceType == null)
            {
                return false;
            }
            Type[] interfaces = instanceType.GetInterfaces();
            foreach (Type currentInterface in interfaces)
            {
                if (currentInterface == interfaceType)
                {
                    return true;
                }
            }
            return false;
        }
        #endregion

        #region UnRegist
        public void UnRegist(object taker)
        {
            UnRegistFromDic(taker, _takersActionAndToken);
            UnRegistFromDic(taker, _takersSubClassActionAndToken);
        }

        public void UnRegist<TMessage>(object taker)
        {
            UnRegist<TMessage>(taker, null, null);
        }

        public void UnRegist<TMessage>(object taker, object token)
        {
            UnRegist<TMessage>(taker, token, null);
        }

        public void UnRegist<TMessage>(object taker, Action<TMessage> action)
        {
            UnRegist<TMessage>(taker, null, action);
        }

        public void UnRegist<TMessage>(object taker, object token, Action<TMessage> action)
        {
            UnRegistFromDic<TMessage>(taker, token, action, _takersActionAndToken);
            UnRegistFromDic<TMessage>(taker, token, action, _takersSubClassActionAndToken);
            CleanUp();
        }

        private void UnRegistFromDic<TMessage>(object taker, object token, Action<TMessage> action, Dictionary<Type, List<WeakActionAndToken>> actionAndTokens)
        {
            Type messageType = typeof(TMessage);
            if (taker == null || actionAndTokens == null || actionAndTokens.Count == 0 || !actionAndTokens.ContainsKey(messageType))
            {
                return;
            }
            lock (actionAndTokens)
            {
                foreach (WeakActionAndToken item in actionAndTokens[messageType])
                {
                    WeakAction<TMessage> removeAction = item.Action as WeakAction<TMessage>;
                    if (removeAction != null && taker == removeAction.Target
                        && (action == null || action == removeAction.Action)
                        && (token == null || token == item.Token))
                    {
                        item.Action.DeleteReference(); //去掉引用
                    }
                }
            }
        }
        private void UnRegistFromDic(object taker,Dictionary<Type, List<WeakActionAndToken>> actionAndTokens)
        {
            if (taker == null || actionAndTokens == null || actionAndTokens.Count == 0)
            {
                return;
            }
            lock (actionAndTokens)
            {
                foreach (Type messageType in actionAndTokens.Keys)
                {
                    foreach (WeakActionAndToken item in actionAndTokens[messageType])
                    {
                        WeakAction removeAction = item.Action as WeakAction;
                        if (removeAction != null && taker == removeAction.Target)
                        {
                            removeAction.DeleteReference(); //去掉引用
                        }
                    }
                }
            }
        }
        #endregion

        #region CleanUp
        /// <summary>
        /// 清除所有的消息队列信息(takerSubClassActionAndToken,takerActionAndToken)
        /// </summary>
        private void CleanUp()
        {
            CleanDictionary(_takersSubClassActionAndToken);
            CleanDictionary(_takersActionAndToken);
            
        }
        /// <summary>
        /// 根据操作和注册的对象是否还存活来判断是否要移除该操作即移除了Dictionary中的值
        /// 移除操作记录相应的MessageType（键）再把相应的键也移除掉
        /// </summary>
        /// <param name="actionAndTokens"></param>
        private void CleanDictionary(Dictionary<Type, List<WeakActionAndToken>> actionAndTokens)
        {
            if (actionAndTokens != null)
            {
                lock (actionAndTokens)
                {
                    List<Type> removeTypes = new List<Type>();
                    foreach (var actionAndToken in actionAndTokens)
                    {
                        List<WeakActionAndToken> removeActionAndTokens = new List<WeakActionAndToken>();
                        foreach (WeakActionAndToken item in actionAndToken.Value)
                        {
                            if (item.Action == null || !item.Action.IsAlive)
                            {
                                removeActionAndTokens.Add(item);
                            }
                        }
                        foreach (WeakActionAndToken removeItem in removeActionAndTokens)
                        {
                            actionAndToken.Value.Remove(removeItem);
                        }
                        if (actionAndToken.Value.Count == 0)
                        {
                            removeTypes.Add(actionAndToken.Key);
                        }
                    }

                    foreach (Type removeType in removeTypes)
                    {
                        actionAndTokens.Remove(removeType);
                    }
                }
            }
        }
        #endregion

        public struct WeakActionAndToken
        {
            public WeakAction Action;
            public object Token;
        }
    }
}
